<template>
    <el-dialog :data-v-id="id"
        class="tnxel-dialog"
        :class="this.theme ? ('theme-' + this.theme) : ''"
        :visible.sync="visible"
        :destroy-on-close="true"
        :append-to-body="true"
        :modal-append-to-body="false"
        :modal="options.modal"
        :close-on-click-modal="options['close-on-click-modal']"
        :close-on-press-escape="options['close-on-press-escape']"
        :show-close="options['show-close']"
        :center="options.center"
        :width="width"
        :top="top"
        :before-close="beforeClose"
        @closed="onClosed">
        <template #title>
            <div class="dialog-title" :class="{'border-bottom': title}" v-html="title" v-if="title"></div>
        </template>
        <div v-if="contentValue" v-html="contentValue"></div>
        <tnxel-dialog-content ref="content" v-bind="contentProps" v-else></tnxel-dialog-content>
        <template #footer>
            <div class="dialog-footer border-top" v-if="buttons && buttons.length">
                <el-button v-for="(button, index) in buttons" :type="button.type" :key="index"
                    :loading="buttonLoadings[index]" @click="btnClick(index)">{{ button.caption || button.text }}
                </el-button>
            </div>
        </template>
    </el-dialog>
</template>

<script>
import $ from 'jquery';

export default {
    name: 'TnxelDialog',
    props: ['title', 'content', 'contentProps', 'buttons', 'theme'],
    data() {
        return {
            id: window.tnx.util.string.uuid32(),
            visible: true,
            contentValue: this.content,
            options: {
                modal: true, // 是否需要遮罩层
                'close-on-click-modal': false, // 是否可以通过点击遮罩层关闭对话框
                'close-on-press-escape': true, // 是否可以通过按下 ESC 关闭对话框
                'show-close': true, // 是否显示关闭按钮
                center: false, // 是否对头部和底部采用居中布局
                width: '512px',
                // 以上均为element的Dialog组件配置项
                onShown: undefined, // 对话框展示后的事件回调
            },
            middleTop: '40vh',
            buttonLoadings: [],
        };
    },
    computed: {
        top() {
            if (typeof this.options.top === 'function') {
                return this.options.top();
            } else if (this.options.top) {
                return this.options.top;
            } else {
                return this.middleTop;
            }
        },
        width() {
            if (typeof this.options.width === 'function') {
                return this.options.width();
            } else if (typeof this.options.width === 'number') {
                return this.options.width + 'px';
            } else {
                return this.options.width;
            }
        },
    },
    mounted() {
        window.tnx.app.eventBus.$on('tnx.error', options => {
            this.buttonLoadings = [];
        });
        this.$nextTick(() => {
            if (this.visible) {
                this.locate();
            }

            if (typeof this.options.onShown === 'function') {
                this.options.onShown.call(this);
            }
        });
    },
    beforeDestroy() {
        window.tnx.app.eventBus.$off('tnx.error');
    },
    components: {
        'tnxel-dialog-content': null,
    },
    methods: {
        locate() {
            let $dialog = $('.el-dialog__wrapper[data-v-id="' + this.id + '"] .el-dialog');
            if ($dialog.length) {
                let top = window.tnx.util.dom.getTopVerticallyCenteredOnPage($dialog[0]);
                this.middleTop = top + 'px';
            }
        },
        btnClick(index) {
            const button = this.buttons[index];
            if (button && typeof button.click === 'function') {
                let result = button.click.call(this.$refs.content, this.close);
                if (result === 'loading') {
                    this.$set(this.buttonLoadings, index, true);
                    return;
                }
                if (result === false) {
                    return;
                }
            }
            this.close();
        },
        close() {
            return new Promise((resolve) => {
                this.beforeClose(() => {
                    this.options.onClosed = window.tnx.util.function.around(this.options.onClosed, function (onClosed) {
                        if (onClosed) {
                            onClosed();
                        }
                        resolve();
                    });
                    this.visible = false;
                });
            });
        },
        beforeClose(done) {
            try {
                if (typeof this.options.beforeClose === 'function') {
                    if (this.options.beforeClose.call(this.$refs.content) === false) {
                        return;
                    }
                }
                // 避免组件内容在关闭时被再次加载，并出现闪现现象
                const height = $('.el-dialog__wrapper[data-v-id="' + this.id + '"] .el-dialog__body').height();
                this.contentValue = '<div style="height: ' + height + 'px"></div>';
                done();
            } catch (e) { // 处理错误，以避免业务代码出错导致对话框无法关闭
                console.error(e);
            }
        },
        onClosed() {
            $('.el-dialog__wrapper[data-v-id="' + this.id + '"]').remove();
            if (typeof this.options.onClosed === 'function') {
                this.options.onClosed.call(this.$refs.content);
            } else {
                this.$emit('closed');
            }
        }
    }
}
</script>

<style>
.tnxel-dialog .el-dialog__header {
    padding: 0;
}

.tnxel-dialog .dialog-title {
    font-size: 16px; /* 与关闭按钮大小一致 */
    padding: 1rem;
}

.tnxel-dialog .dialog-title > :last-child,
.tnxel-dialog .el-dialog__body > div > :last-child {
    margin-bottom: 0;
}

.tnxel-dialog .el-dialog__body {
    padding: 1rem;
    color: inherit;
}

.tnxel-dialog .el-dialog__footer {
    padding: 0;
}

.tnxel-dialog .dialog-footer {
    padding: 1rem;
    display: flex;
    justify-content: flex-end;
}

.tnxel-dialog .dialog-footer .el-button {
    margin-left: 10px;
}
</style>
